/*
 * 项   目：util-mht2hmtl
 * 作   者：罗毅（ymluo）
 * 邮   箱：ymluo@iflytek.com
 * 创建时间：2018/2/1 9:16
 */
package com.roin.util;

import sun.misc.BASE64Decoder;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * QQ聊天记录mht文件转html文件工具类
 * QQ导出的mht文件特别大的时候无法用浏览器打开，此工具专门针对QQ导出聊天记录的Mht文件进行处理，
 * 把mht文件里的图片提取出来，并分割文件为若干个html文件。完成后会在文件输出目录生成images目录，用于保存图片。
 * @version 1.0
 * @author 罗毅(ymluo)
 * @date 2018-02-01 09:17
 */

public class QQMht2HtmlUtil {
    public static void main(String[] args) {
        htm2html("E:\\下载临时\\QQ聊天记录Mht转Html格式\\冰心妹妹(709214995).mht","E:\\下载临时\\QQ聊天记录Mht转Html格式\\我和妹妹多年的聊天\\章节.html",
                5);
        //getImages("D:\\Users\\Roin\\Desktop\\bak\\萍姐(479789227).mht","D:\\Users\\Roin\\Desktop\\bak\\test.html");
//        htm2html("D:\\Users\\Roin\\Desktop\\bak\\萍姐(479789227).mht","D:\\Users\\Roin\\Desktop\\bak\\test.html",5);
    }
    public static Map<String,String> getImages(String mhtFilePath, String htmlFilePath){
        chekcFilePath(mhtFilePath, htmlFilePath);
        File mhtFile = new File(mhtFilePath);
        if (!mhtFile.exists())
            throw new RuntimeException("mht文件不存在，请检查文件路径！");
        //创建图片文件存放目录
        File fileD = new File(htmlFilePath.substring(0,htmlFilePath.lastIndexOf(File.separator))+File.separator+"images");
        if (!fileD.exists())
            fileD.mkdirs();
        BufferedInputStream fis = null;
        BufferedReader reader = null;
        int oneMByte = 1024*1024;
        Map<String,String> imgTypeMap = new HashMap<>();
        try {

            fis = new BufferedInputStream(new FileInputStream(mhtFile));
            reader = new BufferedReader(new InputStreamReader(fis,"utf-8"),5*oneMByte);//用10M的缓冲读取文本文件
            String line = "";

            String imgB="------=_NextPart_";//一个图片开始标记
            String imgT="Content-Type:image/";
            String imgN="Content-Location:{";
            String imgNE="}.dat";
            String imgType = "";
            String imgName = "";

            //提取图片
            while((line = reader.readLine()) != null){
                if (line.indexOf(imgB)!=-1){//一个图片文件内容开始
                    //获取图片类型
                    line=reader.readLine();
                    if (line!=null && line.indexOf(imgT)>-1)
                        imgType = line.substring(line.indexOf(imgT)+imgT.length(),line.length());
                    else
                        continue;
                    //获取图片文件名
                    reader.readLine();
                    line=reader.readLine();
                    if (line!=null)
                        imgName = line.substring(line.indexOf(imgN)+imgN.length(),line.indexOf(imgNE));
                    else
                        continue;
                    reader.readLine();
                    //获取图片内容
                    StringBuffer imgBase64Str = new StringBuffer();
                    while ((line = reader.readLine()) != null){
                        if (line.trim().length()==0){//图片内容提取结束
                            imgTypeMap.put(imgName,imgType);
                            //生成图片
                            generateImage(imgBase64Str.toString(),fileD.getPath()+File.separator+imgName+"."+imgType);
                            break;//继续提取下一张图片
                        }else {
                            imgBase64Str.append(line);
                        }

                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (reader != null) {
                try {
                    reader.close();
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return imgTypeMap;
    }
    /**
     * 把mht文件转成html文件
     * @param mhtFilePath
     * @param htmlFilePath
     * @param htmlFileSize 每个html文件大小，单位是M，默认10M
     * @return boolean
     * @throws
     * @author 罗毅(ymluo)
     * @date 2018-02-01 10:04
     */
    public static boolean htm2html(String mhtFilePath,String htmlFilePath,int htmlFileSize){
        String htmlFileName = chekcFilePath(mhtFilePath, htmlFilePath);
        //提取图片，获得图片类型字典
        Map<String,String> imgMap = getImages(mhtFilePath,htmlFilePath);
        File mhtFile = new File(mhtFilePath);
        if (!mhtFile.exists())
            throw new RuntimeException("mht文件不存在，请检查文件路径！");
        //创建html文件存放目录
        File fileD = new File(htmlFilePath.substring(0,htmlFilePath.lastIndexOf(File.separator)));
        if (!fileD.exists())
            fileD.mkdirs();
        BufferedInputStream fis = null;
        BufferedReader reader = null;
        BufferedOutputStream fos = null;
        BufferedWriter writer = null;
        try {
            int oneMByte = 1024*1024;
            fis = new BufferedInputStream(new FileInputStream(mhtFile));
            reader = new BufferedReader(new InputStreamReader(fis,"utf-8"),5*oneMByte);//用10M的缓冲读取文本文件
            String line = "";
            String headerStr ="<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta http-equiv=\"Content-Type\" " +
                    "content=\"text/html; charset=UTF-8\" /><title>QQ聊天记录</title><style " +
                    "type=\"text/css\">body{font-size:12px; line-height:22px; margin:2px;}td{font-size:12px; " +
                    "line-height:22px;} table img{max-width:300px;}</style></head><body><table width=100% " +
                    "cellspacing=0>";
            String footerStr ="</table></body></html>";
            String table = "<table";
            String tableEnd = "</table";


            boolean findHeader = false;
            boolean findFooter = false;
            int count = 1;
            int fileSize = 0;

            File htmlFile = new File(fileD,htmlFileName+count+".html");
            fos = new BufferedOutputStream(new FileOutputStream(htmlFile));
            writer = new BufferedWriter(new OutputStreamWriter(fos,"utf-8"),5*oneMByte);
            //遍历并拆分正文
            while((line = reader.readLine()) != null){
                //看看是否有图片
                int indexS = line.lastIndexOf(table);
                int indexE = line.lastIndexOf(tableEnd);
                if (!findHeader && indexS!=-1){//找到正文开头
                    findHeader = true;
                    line = line.substring(line.indexOf(">",indexS)+1,line.length());
                    writer.write(headerStr);
                    fileSize +=line.length();
                    //替换图片为本地图片
                    line = replaceImg(line,imgMap);
                    writer.write(line);
                    continue;
                }
                //如果找到了没有找到正文开头，不写入新文件，继续找
                if (!findHeader)
                    continue;
                //说明已经找到正文开头，看一下正文结尾是不是在这一行
                if(!findFooter && indexE!=-1){//找到正文结尾,写入结尾，文件读写结束
                    line = line.substring(0,indexE);
                    //替换图片为本地图片
                    line = replaceImg(line,imgMap);
                    writer.write(line);
                    writer.write(footerStr);
                    break;
                }
                //说明找到正文开头，还没找到正文结尾
                //看看文件是否达到了指定大小
                fileSize +=line.length();
                if (fileSize/oneMByte==htmlFileSize){//达到了指定大小
                    fileSize = 0;
                    //替换图片为本地图片
                    line = replaceImg(line,imgMap);
                    writer.write(line.substring(0,line.lastIndexOf("</tr>")+5));
                    writer.write(footerStr);
                    writer.flush();
                    if (writer!=null) {
                        try {
                            writer.close();
                            if (fos!=null) {
                                try {
                                    fos.flush();
                                    fos.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    count++;
                    //新建下一个文件
                    File f = new File(fileD,htmlFileName+count+".html");
                    fos = new BufferedOutputStream(new FileOutputStream(f));
                    writer = new BufferedWriter(new OutputStreamWriter(fos,"utf-8"),5*oneMByte);

                    writer.write(headerStr);
                    writer.write(line.substring(line.lastIndexOf("</tr>")+5,line.length()));
                    continue;
                }
                //替换图片为本地图片
                line = replaceImg(line,imgMap);
                writer.write(line);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (reader!=null) {
                try {
                    reader.close();
                    if (fis!=null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (writer!=null) {
                try {
                    writer.close();
                    if (fos!=null) {
                        try {
                            fos.flush();
                            fos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return false;
    }

    private static String replaceImg(String line,Map<String,String> imgMap){
        String srcB="src=\"{";
        String srcE="}.dat";
        while (line.indexOf(srcE)!=-1){
            String imgName = line.substring(line.indexOf(srcB)+srcB.length(),line.indexOf(srcE));
            line =  line.replace("{"+imgName+"}.dat","images"+File.separator+imgName+"."+imgMap.get(imgName));
        }
        return line;
    }
    private static String chekcFilePath(String mhtFilePath, String htmlFilePath) {
        if (mhtFilePath==null || mhtFilePath.length()==0)
            throw new RuntimeException("请指定mht文件位置");
        if (htmlFilePath==null || htmlFilePath.length()==0)
            throw new RuntimeException("请指定html文件存放位置");
        int indexTemp = htmlFilePath.lastIndexOf(".");
        String htmlFileName = htmlFilePath.substring(htmlFilePath.lastIndexOf(File.separator),
                (indexTemp==-1?htmlFilePath.length():indexTemp));
        if (htmlFileName==null ||htmlFileName.length()==0)
            throw new RuntimeException("请指定正确的html文件存放位置");
        return htmlFileName;
    }

    /**
     * 将base64编码字符串转换为图片
     * @param imgStr base64编码字符串
     * @param path 图片路径-具体到文件
     * @return boolean
     * @throws
     * @author 罗毅(ymluo)
     * @date 2018-02-01 09:06
     */
    public static boolean generateImage(String imgStr, String path) {
        if (imgStr == null)
            return false;
        OutputStream out=null;
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // 解密
            byte[] b = decoder.decodeBuffer(imgStr);
            // 处理数据
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {
                    b[i] += 256;
                }
            }
            out = new FileOutputStream(path);
            out.write(b);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }finally {
            if (out!=null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
